import * as THREE from 'three';
import * as TWEEN from '@tweenjs/tween.js';
import { Editor } from "./editor";
import { Color } from 'three';
import { OrbitControls} from 'three/examples/jsm/controls/OrbitControls.js';
import {Control, TezeroCommponents, defaultComponents} from './tezero';
import { decompress } from '../index';
import {CSS3DObject,CSS3DSprite,CSS3DRenderer} from 'three/examples/jsm/renderers/CSS3DRenderer.js';

function Player(editor: Editor) {

  const signals = editor.signals;

  const dom = document.createElement('div');
  dom.id = 'tezero-player';
  dom.style.position = 'absolute';
  dom.style.display = 'none';
  //

  const player = new AppPlayer(editor.components);
  dom.appendChild(player.dom);


  window.addEventListener('resize', function () {

    player.setSize(dom.clientWidth, dom.clientHeight);

  });

  signals.windowResize.add(function () {

    player.setSize(dom.clientWidth, dom.clientHeight);

  });

  signals.startPlayer.add(function () {

    dom.style.display = 'block';

    player.load(editor.toJSON());
    player.setSize(dom.clientWidth, dom.clientHeight);
    player.play();

  });

  signals.stopPlayer.add(function () {

    dom.style.display = 'none';

    player.stop();
    player.dispose();

  });

  return dom;

}


class AppPlayer {

  renderer: THREE.WebGLRenderer;
  css3dRenderer: CSS3DRenderer;
  loder = new THREE.ObjectLoader();
  loader = new THREE.ObjectLoader();
  camera?: THREE.Camera;
  scene?: THREE.Scene;
  dom: HTMLElement;
  width: number;
  height: number;

  components?:TezeroCommponents=defaultComponents;

  time?: number;
  startTime?: number;
  prevTime?: number;

  events: any = {
    init: [],
    start: [],
    stop: [],
    keydown: [],
    keyup: [],
    pointerdown: [],
    pointerup: [],
    pointermove: [],
    update: []
  };

  animations: any[] = [];

  controls?:OrbitControls;

  constructor(comps) {
    this.components=comps;
    this.renderer = new THREE.WebGLRenderer({ antialias: true,logarithmicDepthBuffer: true  });
    this.renderer.setPixelRatio(window.devicePixelRatio); // TODO: Use player.setPixelRatio()
    // var vrButton = VRButton.createButton( renderer ); // eslint-disable-line no-undef

    this.css3dRenderer=new CSS3DRenderer();
    this.css3dRenderer.domElement.style.position="absolute";
    this.css3dRenderer.domElement.style.top='0';
    this.css3dRenderer.domElement.style.left='0';

    var dom = document.createElement('div');
    dom.appendChild(this.renderer.domElement);
		dom.appendChild(this.css3dRenderer.domElement);

    this.dom = dom;

    this.width = 500;
    this.height = 500;
  }

  setCamera(value: THREE.Camera) {

    this.camera = value;
    if (this.camera instanceof THREE.PerspectiveCamera) {
      this.camera.aspect = this.width / this.height;
      this.camera.updateProjectionMatrix();
    }

  }

  setScene(value: THREE.Scene) {

    this.scene = value;
  }

  setPixelRatio(pixelRatio: number) {

    this.renderer.setPixelRatio(pixelRatio);

  }

  setSize(width: number, height: number) {

    this.width = width;
    this.height = height;

    if (this.camera instanceof THREE.PerspectiveCamera) {

      this.camera.aspect = this.width / this.height;
      this.camera.updateProjectionMatrix();

    }

    this.renderer.setSize(width, height);
    this.css3dRenderer.setSize(width, height);

  }

  dispatch(array: any, event: any) {

    for (var i = 0, l = array.length; i < l; i++) {

      array[i](event);

    }

  }



  animate() {

    this.time = performance.now();

    try {
      if (this.time !== undefined && this.prevTime !== undefined && this.startTime !== undefined)
        this.dispatch(this.events.update, { time: this.time - this.startTime, delta: this.time - this.prevTime });

    } catch (e: any) {

      console.error((e.message || e), (e.stack || ''));

    }
    TWEEN.update();
    if (this.scene && this.camera){
      this.controls?.update();
      this.renderer.render(this.scene, this.camera);
      this.css3dRenderer.render(this.scene, this.camera);
    }
    this.prevTime = this.time;

  }

  play() {

    // if ( renderer.xr.enabled ) dom.append( vrButton );

    this.startTime = this.prevTime = performance.now();

    document.addEventListener('keydown', this.onKeyDown.bind(this));
    document.addEventListener('keyup', this.onKeyUp.bind(this));
    document.addEventListener('pointerdown', this.onPointerDown.bind(this));
    document.addEventListener('pointerup', this.onPointerUp.bind(this));
    document.addEventListener('pointermove', this.onPointerMove.bind(this));

    this.dispatch(this.events.start, arguments);

    this.renderer.setAnimationLoop(this.animate.bind(this));

  };

  stop() {

    // if ( renderer.xr.enabled ) vrButton.remove();

    document.removeEventListener('keydown', this.onKeyDown.bind(this));
    document.removeEventListener('keyup', this.onKeyUp.bind(this));
    document.removeEventListener('pointerdown', this.onPointerDown.bind(this));
    document.removeEventListener('pointerup', this.onPointerUp.bind(this));
    document.removeEventListener('pointermove', this.onPointerMove.bind(this));

    this.dispatch(this.events.stop, arguments);

    this.renderer.setAnimationLoop(null);

  }

  render(time: number) {
    this.dispatch(this.events.update, { time: time * 1000, delta: 0 /* TODO */ });
    if (this.scene && this.camera){
      this.controls?.update();
      this.renderer.render(this.scene, this.camera);
      this.css3dRenderer.render(this.scene,this.camera);
    }

  }

  dispose() {
    // this.css3dRenderer?.domElement?.remove();
    // this.css3dRenderer=undefined;
    this.renderer.dispose();
    this.css3dRenderer.domElement.firstChild.firstChild.childNodes.forEach(element => {
      element.remove();
    });
    this.camera = undefined;
    this.scene = undefined;
    if(this.controls)
    this.controls.dispose();

  }

  //

  onKeyDown(event: KeyboardEvent) {

    this.dispatch(this.events.keydown, event);

  }

  onKeyUp(event: KeyboardEvent) {

    this.dispatch(this.events.keyup, event);

  }

  onPointerDown(event: PointerEvent) {

    this.dispatch(this.events.pointerdown, event);

  }

  onPointerUp(event: PointerEvent) {

    this.dispatch(this.events.pointerup, event);

  }

  onPointerMove(event: PointerEvent) {

    this.dispatch(this.events.pointermove, event);

  }

  load(json: any,isEdit?:boolean) {
    var project = json.project;

    if (project.vr !== undefined) this.renderer.xr.enabled = project.vr;
    if (project.shadows !== undefined) this.renderer.shadowMap.enabled = project.shadows;
    if (project.shadowType !== undefined) this.renderer.shadowMap.type = project.shadowType;
    if (project.toneMapping !== undefined) this.renderer.toneMapping = project.toneMapping;
    if (project.toneMappingExposure !== undefined) this.renderer.toneMappingExposure = project.toneMappingExposure;

    // 处理图片没有加载失败
		if(json.scene.images){
			json.scene.images.forEach(element => {
				if(element.url==''){
					element.url={};
				}
			});
		}

    this.setScene(this.loader.parse(json.scene));
    
    this.setCamera(this.loader.parse(json.camera));
    

    if(project.background!==undefined&&(this.scene.background==undefined||this.scene.background==null)){
      this.scene.background=new Color(project.background);
    }
    if((project.control!==undefined&&project.control!==null)||project.control===true){
      this.controls = new OrbitControls( this.camera, this.dom );
      
      if(!(project.control instanceof Boolean)){
        var params=project.control as Control;
        if(params.autoRotate!==undefined){
          this.controls.autoRotate=params.autoRotate;
        }
        if(params.autoRotateSpeed!==undefined){
          this.controls.autoRotateSpeed=params.autoRotateSpeed;
        }
        if(params.dampingFactor!==undefined){
          this.controls.dampingFactor=params.dampingFactor;
        }else{
          this.controls.dampingFactor=0.05;
        }
        if(params.enableDamping!==undefined){
          this.controls.enableDamping=params.enableDamping;
        }else{
          this.controls.enableDamping=true;
        }
        if(params.enablePan!==undefined){
          this.controls.enablePan=params.enablePan;
        }
        if(params.enableRotate!==undefined){
          this.controls.enableRotate=params.enableRotate;
        }
        if(params.enableZoom!==undefined){
          this.controls.enableZoom=params.enableZoom;
        }
        if(params.keyPanSpeed!==undefined){
          this.controls.keyPanSpeed=params.keyPanSpeed;
        }
        if(params.maxAzimuthAngle!==undefined){
          this.controls.maxAzimuthAngle=params.maxAzimuthAngle;
        }
        if(params.maxDistance!==undefined){
          this.controls.maxDistance=params.maxDistance;
        }
        if(params.maxPolarAngle!==undefined){
          this.controls.maxPolarAngle=params.maxPolarAngle;
        }
        if(params.maxZoom!==undefined){
          this.controls.maxZoom=params.maxZoom;
        }
        if(params.minAzimuthAngle!==undefined){
          this.controls.minAzimuthAngle=params.minAzimuthAngle;
        }
        if(params.minDistance!==undefined){
          this.controls.minDistance=params.minDistance;
        }
        if(params.minPolarAngle!==undefined){
          this.controls.minPolarAngle=params.minPolarAngle;
        }
        if(params.minZoom!==undefined){
          this.controls.minZoom=params.minZoom;
        }
        if(params.panSpeed!==undefined){
          this.controls.panSpeed=params.panSpeed;
        }
        if(params.rotateSpeed!==undefined){
          this.controls.rotateSpeed=params.rotateSpeed;
        }
        if(params.screenSpacePanning!==undefined){
          this.controls.screenSpacePanning=params.screenSpacePanning;
        }
        if(params.zoomSpeed!==undefined){
          this.controls.zoomSpeed=params.zoomSpeed;
        }
        if(params.zoomToCursor!==undefined){
          this.controls.zoomToCursor=params.zoomToCursor;
        }
      

      }
    }

    this.events = {
      init: [],
      start: [],
      stop: [],
      keydown: [],
      keyup: [],
      pointerdown: [],
      pointerup: [],
      pointermove: [],
      update: []
    };

    var scriptWrapParams = 'player,renderer,scene,camera';
    var scriptWrapResultObj: any = {};

    for (var eventKey in this.events) {

      scriptWrapParams += ',' + eventKey;
      scriptWrapResultObj[eventKey] = eventKey;

    }

    var scriptWrapResult = JSON.stringify(scriptWrapResultObj).replace(/\"/g, '');

    for (var uuid in json.scripts) {

      var object = this.scene?.getObjectByProperty('uuid', uuid);

      if (object === undefined) {

        console.warn('APP.Player: Script without object.', uuid);
        continue;

      }

      var scripts = json.scripts[uuid];

      for (var i = 0; i < scripts.length; i++) {

        var script = scripts[i];

        var functions = (new Function(scriptWrapParams, script.source + '\nreturn ' + scriptWrapResult + ';').bind(object))(this, this.renderer, this.scene, this.camera);

        for (var name in functions) {

          if (functions[name] === undefined) continue;

          if (this.events[name] === undefined) {

            console.warn('APP.Player: Event type not supported (', name, ')');
            continue;

          }

          this.events[name].push(functions[name].bind(object));

        }

      }

    }

    

    this.scene.traverse((obj)=>{
      // 处理css3object
      if(obj.userData&&obj.userData.html){
        const handler=this.components[obj.userData.type];
        if(handler===undefined){
          console.warn(`未知的组件类型: ${obj.userData.type}`);
          return;
        }
        const o=handler(obj.userData.html) as CSS3DObject;
        const index=obj.parent.children.findIndex((o)=>o.uuid===obj.uuid);
        // const o=cssButton(obj.userData.html) as CSS3DObject;
        o.position.copy(obj.position);
        o.scale.copy(obj.scale);
        o.rotation.copy(obj.rotation);
        o.renderOrder = obj.renderOrder;
        const tempId=o.uuid;
        o.uuid=obj.uuid;
        o.name=obj.name;
        obj.uuid=tempId;
				o.userData=obj.userData;
        this.addObject(o,obj.parent,index);
        obj.removeFromParent();
      }
    });
    console.log(this.scene);
    this.render(0);

    this.tweenAnimations();
    this.dispatch(this.events.init, arguments);

  }

  tweenAnimations() {
    this.scene?.traverse(object => {
      
      if (object.userData === undefined || object.userData === null) {
        return;
      }
      if(object.userData.isText){
        if(Number.isNaN(object.rotation.x)){
          object.rotation.x=0;
        }
        if(Number.isNaN(object.rotation.y)){
          object.rotation.y=0;
        }
        object.quaternion.copy(new THREE.Quaternion());
      }
      if (object.userData.animations && Array.isArray(object.userData.animations)) {
        object.userData.animations.forEach((animation: any) => {
          const tween = new TWEEN.Tween((object as any)[animation.key]);
          if (animation.delay !== undefined && animation.delay !== null) {
            tween.delay(animation.delay);
          }
          if(animation.key==='rotation'){
            const targetRotation = {x:null,y:null,z:null };
            if(animation.to.x){
              targetRotation.x = animation.to.x + Math.PI*2 
            }
            if(animation.to.y){
              targetRotation.y = animation.to.y + Math.PI*2 
            }
            if(animation.to.z){
              targetRotation.z = animation.to.z + Math.PI*2 
            }
            tween.to(targetRotation, animation.time);
          }else{
            tween.to(animation.to, animation.time);
          }
          
          if (animation.repeat !== undefined && animation.repeat !== null) {
            if (animation.repeat < 0) {
              animation.repeat = Infinity;
            }
            tween.repeat(animation.repeat);
          }
          if (animation.yoyo === true) {
            tween.yoyo(true);
          }
          tween.easing(animation.easing);
          if (animation.start === true) {
            tween.start();
          }
          
          this.animations.push(tween);
        });
      }
    });
  }


  
  addObject(object: THREE.Object3D, parent?: THREE.Object3D, index?: number) {
		// this.outline.addObject(object,parent,index);
		var scope = this;

		if (parent === undefined) {

			this.scene.add(object);

		} else if (index != undefined) {

			parent.children.splice(index, 0, object);
			object.parent = parent;

		}
		
    this.render(0);

	}

}


class View{
  private el:HTMLElement;
  private player:AppPlayer;
  private components?:TezeroCommponents=defaultComponents;

  constructor(el:HTMLElement){
    this.el=el;
    this.player = new AppPlayer(this.components);
    el.appendChild(this.player.dom);
    this.resize();
    this.player.play();
  }

  resize(){
    this.player.setSize(this.el.clientWidth, this.el.clientHeight);
  }

  open(data:Uint8Array){
    decompress(data).then(d=>{
      const json=new TextDecoder().decode(d);
      this.openJson(JSON.parse(json));
    }).catch(error=>{
      throw error;
    });
    /* const json=new TextDecoder().decode(decompress(data));
    try {
      this.openJson(JSON.parse(json));
    } catch (error) {
      throw error; 
    } */
  }

  openJson(json:any){
    this.player.load(json,false);
  }

  /**
   * 注册组件
   * @param compoennts 物体组件数据
   */
  register(compoennts:TezeroCommponents){
    if(this.components)
    Object.assign(this.components,compoennts);
    this.player.components=this.components;
  }

}

export { Player,View };